package com.suitbim.commandserver.commandserver.client;

import com.google.gson.Gson;
import com.suitbim.commandserver.commandserver.manage.FileManage;
import com.suitbim.commandserver.commandserver.util.BimApiUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.*;
import java.util.HashMap;
import java.util.concurrent.ThreadPoolExecutor;

public class RevitCommandClient extends AbstractClient {

    @Autowired
    private BimApiUtil bimApiUtil;

    @Autowired
    private FileManage fileManage;

    @Value("${redis-message-queue.response-queue}")
    private String commandResponseQueue;

    @Value("${redis-message-queue.threeD-tiles-convert-command}")
    private String threeDTilesConvertCommand;

    @Value("${mode-read-application.revit.path}")
    private String revitPath;

    @Value("${mode-read-application.revit.inputPath}")
    private String revitInputPath;

    public RevitCommandClient(Gson gson, ThreadPoolExecutor threadPoolExecutor, RedisTemplate<String, String> redisTemplate, String taskQueueTopic) {
        super(gson, threadPoolExecutor, redisTemplate, taskQueueTopic);
    }

    @Override
    public String getClientPath() {
        return revitPath;
    }

    @Override
    public String getInputPath() {
        return revitInputPath;
    }

    @Override
    public File currentFile(Command command) {
        String[] split = command.getUrl().split("/");
        String fileName = split[split.length - 1];
        File file = new File(getInputPath() + "/" + fileName);

        return file;
    }

    @Override
    public String buildCommand(Command command) {
        String[] split = command.getUrl().split("/");
        String fileName = split[split.length - 1];
        String account = command.getAccount();
        String password = command.getPassword();
        Long projectId = command.getProjectId();
        Long sceneId = command.getSceneId();
        String description = command.getDescription();


        String cmd = revitPath + " -u -i " + fileName + " -acc " + account + " -pwd " + password +
                " -pid " + projectId + " -sid " + sceneId + " -d " + description;

        if (command.isAddMode()) cmd += " -add";
        if (command.isUploadProperty()) cmd += " -prop";
        return cmd;
    }

    @Override
    public boolean execute(Command command) {
        Runtime runtime = Runtime.getRuntime();

        Long sceneId = command.getSceneId();

        log.info("prepare update scene:" + command.getName() + sceneId);
        Long optionId = command.getOptionId();
        String name = command.getName();

        int version = 1;
        boolean success = false;
        String destination = command.getDestination();

        try {
            fileManage.downLoadFile(command.getUrl(), getInputPath());
        } catch (Exception e) {
            e.printStackTrace();
            sendSuccessMessage(optionId, sceneId, name, false, version, destination);
            return false;
        }

        if (sceneId == null) {
            log.info("create new scene:" + command.getName());
            try {
                sceneId = bimApiUtil.createScene(command.getAccount(), command.getPassword(), command.getProjectId().toString(), command.getName());
                command.setSceneId(sceneId);
            } catch (Exception e) {
                e.printStackTrace();
                sendSuccessMessage(optionId, sceneId, name, false, version, destination);
                return false;
            }
        }
        String cmd = buildCommand(command);

        log.info("run command:" + cmd);
        try {
            Process process = runtime.exec(cmd);

            InputStream inputStream = process.getInputStream();
            InputStream errorStream = process.getErrorStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            BufferedReader errorReader = new BufferedReader(errorStreamReader);
            String print;

            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        while (errorReader.readLine() != null) {

                        }
                        errorReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };

            threadPoolExecutor.execute(runnable);

            while (process.isAlive()) {
                print = bufferedReader.readLine();
                if (print == null) continue;
                else {
                    print = print.toLowerCase().trim();
                    if (print.equals("code:1")) {
                        success = true;
                    } else if (print.startsWith("scenever:")) {
                        String id = print.split(":")[1];
                        version = Integer.valueOf(id);
                    }
                    log.info("client print:" + print);
                }
            }
            bufferedReader.close();
            process.destroy();
            log.info("client execute finish");
        } catch (Exception e) {
            e.printStackTrace();
            sendSuccessMessage(optionId, sceneId, name, false, version, destination);
            return false;
        }

        fileManage.deleteFile(this.currentFile(command));
        sendSuccessMessage(optionId, sceneId, name, success, version, destination);
        send3dTilesConvertMessage(sceneId, version, null);
        return true;
    }

    private void send3dTilesConvertMessage(Long sceneId, Integer version, Integer lodLevel) {
        HashMap map = new HashMap();
        map.put("source", "bim");
        map.put("biz", "bim_trans");
        map.put("option", "3d_tiles_convert");
        HashMap param = new HashMap();
        map.put("param", param);
        param.put("sceneId", sceneId);
        param.put("version", version);
        param.put("lodLevel", lodLevel);
        String response = gson.toJson(map);

        redisTemplate.opsForList().rightPush(threeDTilesConvertCommand, response);

        log.info("send request:" + response);
    }

    private void sendSuccessMessage(Long optionId, Long sceneId, String name, boolean success, int version, String destination) {
        HashMap map = new HashMap();
        map.put("source", "bim");
        map.put("biz", "bim_trans");
        map.put("option", "response");
        HashMap param = new HashMap();
        map.put("param", param);
        param.put("optionId", optionId);
        param.put("sceneId", sceneId);
        param.put("name", name);
        param.put("success", success);
        param.put("version", version);
        String response = gson.toJson(map);
        if (destination != null)
            redisTemplate.opsForList().rightPush(destination, response);
        else
            redisTemplate.opsForList().rightPush(commandResponseQueue, response);

        log.info("send response:" + response);
    }
}
